/*
** Copyright (c) 2010  Kimmo 'Rainy' Pekkola
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program.  If not, see http://www.gnu.org/licenses.
*/

#include "scanthread.h"
#include "mainwindow.h"

//-----------------------------------------------------------------------------
/**
** Constructor
**
** \param pParent The parent for the object.
** \param strFolder The folder to be scanned.
*/
CScanThread::CScanThread(QWidget* pParent, QString strFolder) : QThread(pParent)
{
    m_strFolder = strFolder;
    m_bCancelled = false;
    m_Files = 0;
    m_Folders = 0;
}

//-----------------------------------------------------------------------------
/**
** Destructor
*/
CScanThread::~CScanThread()
{
}

//-----------------------------------------------------------------------------
/**
** Cancels the operation.
*/
void CScanThread::cancel()
{
    QMutexLocker lock(&m_mutex);
    m_bCancelled = true;
}

//-----------------------------------------------------------------------------
/**
** Returns true if the operation has been cancelled.
*/
bool CScanThread::isCancelled()
{
    QMutexLocker lock(&m_mutex);
    return m_bCancelled;
}

//-----------------------------------------------------------------------------
/**
** The thread function. Reads the folder's content recursively and reports
** the disk usage. Alternatively can scan also the size of the installed
** applications
*/
void CScanThread::run()
{
    QTime timer;
    timer.start();

    if (m_strFolder.isEmpty())
    {
        // Scan installed programs
        emit folderFound(tr("Packages"), 0);
        scanInstallations();
        emit folderScanned();

        qDebug() << "Found" << m_Folders << "packages with" << m_Files << "files";
    }
    else
    {
        // Scan the folder contents
        emit folderFound(m_strFolder, 0);
        scanFolder(m_strFolder, 0);
        emit folderScanned();

        qDebug() << "Found" << m_Files << "files and" << m_Folders << "folders";
    }

    emit progressValue(QString(), -1);

    qDebug() << "Total scan time: " << timer.elapsed();
}

//-----------------------------------------------------------------------------
/**
** Scans the given folder's files recursively.
**
** \param strFolder The folder to be scanned.
** \param level The current hierarchy level.
*/
void CScanThread::scanFolder(const QString& strFolder, int level)
{
    emit progressMaximum(0);        // Show busybar

    QDir dir(strFolder);
    dir.setFilter(QDir::Files | QDir::Dirs | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot);

    QFileInfoList list = dir.entryInfoList();
    for (int i = 0; i < list.size() && !isCancelled(); ++i)
    {
        QFileInfo fileInfo = list.at(i);

        if (fileInfo.isDir())
        {
            emit progressValue(fileInfo.baseName(), 0);

            emit folderFound(fileInfo.absoluteFilePath(), level);

            //qDebug() << fileInfo.absoluteFilePath();

            if (!CMainWindow::getMountPoints().contains(fileInfo.absoluteFilePath()))
            {
                scanFolder(fileInfo.absoluteFilePath(), level + 1);
            }
            emit folderScanned();
            m_Folders++;
        }
        else
        {
            emit fileFound(fileInfo.fileName(), fileInfo.absoluteFilePath(), fileInfo.size(), level);
            m_Files++;
        }
    }
}

//-----------------------------------------------------------------------------
/**
** Scans the installed applications and reports their total disk usage.
*/
void CScanThread::scanInstallations()
{
    emit progressMaximum(0);        // Show busybar

    QDir dir("/var/lib/dpkg/info");
    dir.setFilter(QDir::Files);
    dir.setNameFilters(QStringList() << "*.list");

    QFileInfoList list = dir.entryInfoList();

    emit progressMaximum(list.size());

    int count = 0;
    for (int i = 0; i < list.size() && !isCancelled(); ++i)
    {
        if (isCancelled())
        {
            break;
        }

        QFileInfo fileInfo = list.at(i);

        QString strBase = fileInfo.fileName();
        int pos = strBase.lastIndexOf('.');
        if (pos != -1)
        {
            strBase = strBase.left(pos);
        }

        emit progressValue(strBase, count++);

        emit folderFound(strBase, 0);
        readPackageFiles(fileInfo.absoluteFilePath());
        emit folderScanned();
    }
}

//-----------------------------------------------------------------------------
/**
** Reads the contents for the given package. emits fileFound signals for
** each found file in the package.
**
** \param strPackage File path to the package file.
*/
void CScanThread::readPackageFiles(QString strPackage)
{
    m_Folders++;

    QFile file(strPackage);
    if (file.open(QFile::ReadOnly))
    {
        QString arrayData = file.readAll();
        QList<QString> listLines = arrayData.split('\n');
        for (int i = 0; i < listLines.size(); i++)
        {
            QString array = listLines[i];
            // This is a folder if the next line has the same beginning
            if (i + 1 < listLines.size())
            {
                if (listLines[i + 1].startsWith(array) || array == "/.")
                {
                    continue;
                }
            }

            QFileInfo fileInfo(array);
            if (fileInfo.isFile() && !fileInfo.isSymLink())
            {
                emit fileFound(array, array, fileInfo.size(), 0);
                m_Files++;
            }
        }
    }
}

// EOF
